<script setup lang="ts" generic="T extends Record<string, any> = Record<string, any>">
import {computed, type CSSProperties, provide, ref} from "vue";
import type FormProp from "@/components/form/type/FormProp.ts";
import type FormEmit from "@/components/form/type/FormEmit.ts";
import type FormItem from "@/components/form/type/FormItem.ts";
import type ProcessedFormItem from "@/components/form/type/ProcessedFormItem.ts";
import TFormItem from "./t-form-item.vue"
import type InputComponent from "@/components/input/type/InputComponent.ts";
import {calculateTextWidth} from "@/components/strs.ts";

const $emit = defineEmits<FormEmit<T>>()
const $prop = withDefaults(defineProps<FormProp<T>>(), {
  labelWidth: 80,
  labelPosition: 'left',
  columns: 24,
  gap: 0
})

const labelWidth = ref($prop.labelWidth)

/**
 * 容器样式，设置 Grid 布局
 */
const containerStyle = computed<CSSProperties>(() => {
  // 处理间距：可以是数字或 [水平, 垂直] 数组
  const gap = Array.isArray($prop.gap)
      ? `${$prop.gap[0]}px ${$prop.gap[1]}px`
      : `${$prop.gap}px`

  return {
    display: 'grid',
    gridTemplateColumns: `repeat(${$prop.columns}, 1fr)`,
    gap,
    gridAutoRows: 'minmax(45px, max-content)',
    alignItems: 'center'
  }
})

/**
 * 将schema数组按行分组
 */
const rowGroups = computed(() => {
  const items = $prop.schema

  const rowGroups = new Map<number, FormItem<T>[]>()

  // 将所有元素按行分组
  items.forEach(item => {
    const row = item.row
    if (!rowGroups.has(row)) {
      rowGroups.set(row, [])
    }
    rowGroups.get(row)!.push(item)
  })

  return rowGroups
})

/**
 * 处理表单项数组，计算每个项的实际位置<br/>
 * 核心逻辑：自动计算未指定 col 和 span 的元素位置
 */
const processedItems = computed<ProcessedFormItem<T>[]>(() => {
  const processed: ProcessedFormItem<T>[] = []

  // 处理每一行的元素
  rowGroups.value.forEach((rowItems) => {
    // 记录当前行已占用到的列位置（不包含 offset）
    let currentCol = 0

    // 处理当前行的每个元素
    rowItems.forEach((item, index) => {
      //计算当前item的label长度
      const width = calculateTextWidth(item.label, 14)
      if (width > labelWidth.value) {
        labelWidth.value = width
      }

      // 使用局部变量，避免修改原始数据
      let offset = item.offset || 0

      // 校验offset准确性
      if (item.offset && item.offset < 0) {
        console.warn(`字段【${item.key}】的offset属性为负数【${item.offset}】，将被调整为0`)
        offset = 0
      }

      // 计算实际的列位置
      const actualCol = currentCol + offset

      // 校验列位置准确性
      if (actualCol >= $prop.columns) {
        console.warn(`字段【${item.key}】的起始列【${actualCol}】超过了总列数【${$prop.columns}】，该字段将被忽略`)
        return // 跳过这个元素
      }

      // 计算实际占用的列数
      let actualSpan: number

      //如果指定了该表单项横跨的列数
      if (item.span) {
        // 如果指定了 span，直接使用（但要验证）
        actualSpan = item.span

        // 验证 colSpan 的合理性
        if (actualSpan <= 0) {
          console.warn(`字段【${item.key}】的colSpan【${actualSpan}】无效，将使用默认值1`)
          actualSpan = 1
        }
      }
      //如果没有指定表单项横跨的列数，则根据当前行剩余空间智能计算
      else {
        // 查找当前行中还有多少个没有指定 span 的元素
        const remainingItemsWithoutSpan = rowItems
            .slice(index)
            .filter(it => it.span === undefined).length

        if (remainingItemsWithoutSpan > 0) {
          // 计算剩余空间（要考虑 offset）
          const remainingSpace = $prop.columns - actualCol
          // 平均分配给没有指定 span 的元素
          actualSpan = Math.max(1, Math.floor(remainingSpace / remainingItemsWithoutSpan))
        } else {
          // 如果是最后一个元素，占满剩余空间
          actualSpan = $prop.columns - actualCol
        }
      }

      // 确保不超出边界
      if (actualCol + actualSpan > $prop.columns) {
        const originalSpan = actualSpan
        actualSpan = $prop.columns - actualCol
        console.warn(`字段【${item.key}】的colSpan【${originalSpan}】导致溢出，已调整为【${actualSpan}】`)
      }

      // 如果调整后的 span 小于等于 0，跳过这个元素
      if (actualSpan <= 0) {
        console.warn(`字段【${item.key}】没有可用空间，将被忽略`)
        return
      }

      // 生成 CSS Grid 样式
      const style: CSSProperties = {
        gridColumn: `${actualCol + 1} / span ${actualSpan}`,
        gridRow: `${item.row + 1} / span ${item.rowSpan || 1}`
      }

      //const labelWidth = $prop.labelWidth

      // 将处理后的元素添加到结果数组
      processed.push({
        ...item,
        //labelWidth,
        actualCol,
        actualSpan,
        style
      })

      // 更新当前行的占用位置，供下一个元素使用
      currentCol = actualCol + actualSpan
    })
  })

  return processed
})

// 收集所有input表单组件的引用
const formComponents: InputComponent[] = []

// 注册组件到表单
const registerComponent = (component: InputComponent) => {
  formComponents.push(component)
}

// 从表单中注销组件
const unregisterComponent = (component: InputComponent) => {
  const index = formComponents.findIndex(c => c === component)
  if (index > -1) {
    formComponents.splice(index, 1)
  }
}

// 提供注册和注销方法给子组件
provide('register', registerComponent)
provide('unregister', unregisterComponent)

// 暴露给外部的校验方法
const validate = async (): Promise<boolean> => {
  // 对所有注册的组件进行校验
  const validationPromises = formComponents.map(component => component.validate())
  const results = await Promise.all(validationPromises)

  // 如果所有校验都通过，返回 true
  return results.every(result => result)
}

const handleSubmit = () => {
  $emit('submit', $prop.modelValue)
}

const handleReset = () => {
  $emit('reset', $prop.modelValue)
}

// 暴露方法给外部使用
defineExpose({
  validate,
  // 可以暴露其他有用的方法
  getFormComponents: () => formComponents,
  clearAllErrors: () => formComponents.forEach(c => c.clearError())
})
</script>

<template>
  <form class="form-container" :style="containerStyle" @submit.prevent="handleSubmit" @reset.prevent="handleReset">
    <t-form-item v-for="item in processedItems" :key="item.key" :item="item"
                 :label-width="labelWidth"
                 :label-position="$prop.labelPosition"
                 :model="$prop.modelValue"
                 :style="item.style">
      <!-- 传递插槽 -->
      <template v-if="item.type === 'slot'" #[item.key]="slotData">
        <slot :name="item.key" :item="slotData" :model="$prop.modelValue"/>
      </template>
    </t-form-item>

  </form>
</template>

<style scoped>

.form-container {
  margin: 10px 0;
  width: 100%;
  box-sizing: content-box;
}

</style>